package user_api

import (
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"giv-vue-blog/global"
	"giv-vue-blog/models"
	"giv-vue-blog/models/res"
	"giv-vue-blog/plugins/email"
	"giv-vue-blog/utils/jwts"
	"giv-vue-blog/utils/pwd"
	"giv-vue-blog/utils/random"
	"regexp"
)

/*
GetCodesRequest
TODO: Session 的工作流程:

	为了区分客户端请求，服务端会给具体的客户端生成身份标识session，然后客户端每次向服务器发请求的时候，
都带上这个“身份标识”，服务器就知道这个请求来自于谁了
	当客户端浏览器第一次访问服务器并发送请求时，服务器端会创建一个 session 对象，生成一个类似于 key,value 的键值对，
然后将 value 保存到服务器 将 key(cookie)返回到浏览器(客户)端,浏览器下次访问时会携带 key(cookie)，找到对应的 session(value)
Sessions: 认证通过后需要将用户的session数据保存在内存中，随着认证用户的增加，内存开销会大 因此不太建议使用session
*/
// GetCodesRequest 绑定邮箱的请求
type GetCodesRequest struct {
	Email    string  `json:"email" binding:"required,email" msg:"邮箱非法"`
	PassWord string  `json:"password" msg:"请输入密码"`
	Code     *string `json:"code"`
}

// UserBindEmailSession session与邮箱绑定
func (UserApi) UserBindEmailSession(c *gin.Context) {
	_claims, _ := c.Get("claims")
	claims := _claims.(*jwts.CustomClaims)
	//用户绑定邮箱  第一次输入是 邮箱
	//TODO: 绑定结构体 后台会给这个邮箱发送验证码
	var cr GetCodesRequest
	if err := c.ShouldBindJSON(&cr); err != nil {
		res.FailedWithError(err, &cr, c)
		return
	}
	//TODO:  设置session
	session := sessions.Default(c)
	//设置session过期时间
	session.Options(sessions.Options{
		MaxAge: 3600 * 6, //6hrs
	})
	if cr.Code == nil {
		//第一次 后台发送验证码
		//生成四位验证码 将验证码存入session
		code := random.Code(4)
		//TODO: 将email和code写入session并保存 一定要记得保存 key/value键值对形式
		session.Set("valid_email", cr.Email)
		session.Set("valid_code", code)
		err := session.Save()
		if err != nil {
			global.Log.Error(err)
			res.FailedWithMsg("session错误", c)
			return
		}
		// TODO: 这里调用发送验证码的邮箱接口 发送验证码
		err = email.NewCode().Send(cr.Email, "你的验证码是: "+code)
		if err != nil {
			global.Log.Error(err)
		}
		res.OkWithMsg("验证码已发送,请查收", c)
		return
	}
	//TODO: 第二次 输入邮箱，验证码 密码 从
	code := session.Get("valid_code")
	//TODO: 验证session存储的code是否等于输入的code
	if code != *cr.Code {
		res.FailedWithMsg("验证码错误", c)
		return
	}
	var user models.UserModel
	err := global.DB.Take(&user, claims.UserID).Error
	if err != nil {
		res.FailedWithMsg("用户不存在", c)
		return
	}
	//TODO: 这里可以使用密码校验器 0618-2258已修改 采用校验器绑定的方法
	if ok := regexp.MustCompile(`^[0-9a-zA-Z!@#$%^&*()_+|<>,.?/~\[\]{}\-=:;'"]{6,12}$`).MatchString(cr.PassWord); !ok {
		res.FailedWithMsg("新密码不符合要求", c)
		return
	}
	hashPwd := pwd.HashAndSalt(cr.PassWord)
	//TODO: 第一次的邮箱和第二次的邮箱也要做一致性校验
	// TODO: 校验第一次输入的邮箱和第二次输入的邮箱是否一致 0618-2339 将第一次的邮箱保存到session中，在第二次输入时再从session中取出来进行比较
	if session.Get("valid_email") != cr.Email {
		res.FailedWithMsg("邮箱不一致", c)
		return
	}
	err = global.DB.Model(&user).Updates(map[string]any{
		"email":    session.Get("valid_email"),
		"password": hashPwd,
	}).Error
	if err != nil {
		global.Log.Error(err)
		res.FailedWithMsg("绑定邮箱失败", c)
		return
	}
	//TODO: 完成绑定
	res.OkWithMsg("邮箱绑定成功", c)
	return
}
